using System;
using System.Collections.Generic;
using System.Windows;
using ChessKit.ChessLogics;
using System.Linq;
using ChessKit.Common;

namespace ChessKit.ViewModel
{
  public class BoardViewModel : IDispositionProvider, IGameControl, ILegalMovesProvider
  {
    public BoardViewModel()
    {
      LastMoveCells = new CollectionColorizer(CellStyles.IsLastMoveLocationProperty);
    }

    private Board _board = Board.StartPosition;

    public IEnumerable<BoardLocation> PossibleLocations
    {
      get { return BoardLocation.All; }
    }

    public PieceColor SideOnMove
    {
      get { return _board.SideOnMove; }
    }

    public IPiece GetPiece(BoardLocation location)
    {
      return _board.GetPieceAt(Pos(location));
    }

    public event EventHandler<BoardMovesEventArgs> Changed;
    public void InvokeChanged(BoardMovesEventArgs e)
    {
      LastMoveCells.SetLocations(
        e.Moves.Where(arr => arr.Length == 2).SelectMany(m => m));

      var handler = Changed;
      if (handler != null) handler(this, e);
    }

    void IGameControl.Move(BoardLocation from, BoardLocation to)
    {
      Move(from, to, PieceType.Queen);
      OnMoved(new MoveEventArgs(new Move(Pos(from), Pos(to), PieceType.Queen)));
    }

    public void Move(BoardLocation from, BoardLocation to)
    {
      Move(from, to, PieceType.Queen);
    }

    void IGameControl.Move(BoardLocation from, BoardLocation to, PieceType promotion)
    {
      Move(from, to, promotion);
      OnMoved(new MoveEventArgs(new Move(Pos(from), Pos(to), PieceType.Queen)));
    }

    public event EventHandler<MoveEventArgs> Moved;

    public void OnMoved(MoveEventArgs e)
    {
      EventHandler<MoveEventArgs> handler = Moved;
      if (handler != null) handler(this, e);
    }

    public void Move(BoardLocation from, BoardLocation to, PieceType promotion)
    {
      Move(Pos(from), Pos(to), promotion);
    }
    public void Move(Position from, Position to, PieceType promotion)
    {
      var board = _board.MakeMove(new Move(from, to, promotion));

      var move = board.PrevMove;
      if (move.ValidationProgress == ValidationProgress.Invalid) return;
      _board = board;
      Notify(move);
    }
    private void Notify(Move move)
    {
//      Console.Write("{0}-{1} ", move.From.ToString(), move.To.ToString());
      var hint = move.Hint;
      if ((hint & MoveHint.EnPassant) != 0)
      {
        InvokeChanged(new BoardMovesEventArgs(new [] {Pos(move.From), Pos(move.To)},
          new[]{BoardLocation.Cell(move.To.X + 1, move.From.Y + 1)}));
//        Console.WriteLine("EnPassant");
      }
      else
        switch (hint)
        {
          case MoveHint.WhiteKingCastling:
            InvokeChanged(new BoardMovesEventArgs(new[] { Pos(move.From), Pos(move.To) },
              new[] { Pos(Position.Parse("h1")), Pos(Position.Parse("f1")) }));
//            Console.WriteLine("WhiteKingCastling");
            break;
          case MoveHint.BlackKingCastling:
            InvokeChanged(new BoardMovesEventArgs(new[] { Pos(move.From), Pos(move.To)},
              new[] { Pos(Position.Parse("h8")), Pos(Position.Parse("f8"))}));
//            Console.WriteLine("BlackKingCastling");
            break;
          case MoveHint.WhiteQueenCastling:
            InvokeChanged(new BoardMovesEventArgs(new[] { Pos(move.From), Pos(move.To)},
              new[] { Pos(Position.Parse("a1")), Pos(Position.Parse("d1"))}));
//            Console.WriteLine("WhiteQueenCastling");
            break;
          case MoveHint.BlackQueenCastling:
            InvokeChanged(new BoardMovesEventArgs(new[] { Pos(move.From), Pos(move.To)},
              new[] { Pos(Position.Parse("a8")), Pos(Position.Parse("d8"))}));
//            Console.WriteLine("BlackQueenCastling");
            break;
          default:
            InvokeChanged(new BoardMovesEventArgs(new[] { Pos(move.From), Pos(move.To)}));
            break;
        }
    }
    private void NotifyReverse(Move move)
    {
      //      Console.Write("{0}-{1} ", move.From.ToString(), move.To.ToString());
      var hint = move.Hint;
      if ((hint & MoveHint.EnPassant) != 0)
      {
        InvokeChanged(new BoardMovesEventArgs(new[] { Pos(move.To), Pos(move.From) },
          new[] { BoardLocation.Cell(move.To.X + 1, move.From.Y + 1) }));
        //        Console.WriteLine("EnPassant");
      }
      else
        switch (hint)
        {
          case MoveHint.WhiteKingCastling:
            InvokeChanged(new BoardMovesEventArgs(new[] { Pos(move.To), Pos(move.From) },
              new[] { Pos(Position.Parse("f1")), Pos(Position.Parse("h1")) }));
            //            Console.WriteLine("WhiteKingCastling");
            break;
          case MoveHint.BlackKingCastling:
            InvokeChanged(new BoardMovesEventArgs(new[] { Pos(move.To), Pos(move.From) },
              new[] { Pos(Position.Parse("f8")), Pos(Position.Parse("h8")) }));
            //            Console.WriteLine("BlackKingCastling");
            break;
          case MoveHint.WhiteQueenCastling:
            InvokeChanged(new BoardMovesEventArgs(new[] { Pos(move.To), Pos(move.From) },
              new[] { Pos(Position.Parse("d1")), Pos(Position.Parse("a1")) }));
            //            Console.WriteLine("WhiteQueenCastling");
            break;
          case MoveHint.BlackQueenCastling:
            InvokeChanged(new BoardMovesEventArgs(new[] { Pos(move.To), Pos(move.From) },
              new[] { Pos(Position.Parse("d8")), Pos(Position.Parse("a8")) }));
            //            Console.WriteLine("BlackQueenCastling");
            break;
          default:
            InvokeChanged(new BoardMovesEventArgs(new[] { Pos(move.To), Pos(move.From) }));
            break;
        }
    }

    public IEnumerable<MoveTarget> GetLegalMoves(BoardLocation from)
    {
      for (var i = 0; i < 8; i++)
        for (var j = 0; j < 8; j++)
        {
          var move = new Move(Pos(from), new Position(i, j));
          var makeMove = _board.MakeMove(move);
          if (makeMove.PrevMove.ValidationProgress == ValidationProgress.Valid)
          {
            if (makeMove.PrevMove.Warning == ValidationWarning.None)
            {
              yield return new MoveTarget(
                BoardLocation.Cell(i + 1, j + 1),
                (makeMove.PrevMove.Hint & MoveHint.Promotion) != 0);
            }
          }
        }
    }

    #region ' Random '


    public void RandomMove()
    {
      BoardLocation from;
      List<MoveTarget> legalMoves;
      do
      {
        from = GetRandomPiece();
        legalMoves = GetLegalMoves(from).ToList();
      } while (legalMoves.Count == 0);

      var move = legalMoves[Rnd.Next(legalMoves.Count)];
      Move(from, move.Location, PieceType.Queen);
    }

    private static readonly Random Rnd = new Random();

    BoardLocation GetRandomPiece()
    {
      BoardLocation randomLocation;
      do
      {
        randomLocation = GetRandomLocation();
      } while (GetPiece(randomLocation) == null);
      return randomLocation;
    }

    private static BoardLocation GetRandomLocation()
    {
      return BoardLocation.Cell(Rnd.Next(1, 9), Rnd.Next(1, 9));
    }


    #endregion

    private static Position Pos(BoardLocation boardLocation)
    {
      return new Position(boardLocation.X - 1, boardLocation.Y - 1);
    }

    private static BoardLocation Pos(Position pos)
    {
      return BoardLocation.Cell(pos.X + 1, pos.Y + 1);
    }

    public void CopyFen()
    {
      for (int i = 0; i < 4; i++)
      {
        try
        {
          Clipboard.SetText(_board.ToFenString());
          return;
        }
        catch (Exception e)
        {
          Console.WriteLine(e);
        }
      }
  
    }

    public void LoadFen(string fen)
    {
      _board = Board.FromFenString(fen);
    }

    public void Undo()
    {
      if (_board.Prev == null) return;
      var move = _board.PrevMove;
      _board = _board.Prev;
      NotifyReverse(move);
    }
    public CollectionColorizer LastMoveCells { get; private set; }
  }
}